package service

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/fatih/structs"
	uuid "github.com/satori/go.uuid"

	"awesomeProject/app/response"

	"awesomeProject/app/model"
	"awesomeProject/app/request"
	"awesomeProject/global"
	"awesomeProject/util"
)

type UserSrv struct{}

var shopSrv ShopSrv

// LoginWithUsername Login 登录
func (r *UserSrv) LoginWithUsername(username, password, clientIp string, userAgent string) response.Resp {
	//记录登录日志信息
	//global.LoggerOut(userAgent)
	//global.LoggerOut(clientIp)
	ipLocation := util.GetIpLocaton(clientIp)   //获取ip位置信息
	//global.LoggerOut(ipLocation)


	var configSrv ConfigSrv
	configAllLimitRespData := util.GetRespData(configSrv.GetAllLimit())
	configAllowIp := configAllLimitRespData.Data.(map[string]interface{})["allow_ip"].(string)   //全站配置: 授权ip
	configBanIp := configAllLimitRespData.Data.(map[string]interface{})["ban_ip"].(string)   //全站配置: 禁止ip
	//configIpRemark := configAllLimitRespData.Data.(map[string]interface{})["ip_remark"].(string)   //全站配置: ip备注信息
	//global.LoggerOut(configIpRemark)

	arrConfigAllowIp := util.GetIpData(configAllowIp)
	arrConfigBanIp := util.GetIpData(configBanIp)

	//global.LoggerOut(arrConfigAllowIp)
	//global.LoggerOut(arrConfigBanIp)

	//global.LoggerOut(ip)
	var user model.User
	global.Gorm.Where("username = ?", username).First(&user)
	if user.ID <= 0 {
		util.AddLoginlog(username, "登录失败:用户不存在", clientIp, ipLocation, userAgent)
		return response.ErrRecordNotFound.SetMsg("用户不存在")
	}

	// 密码验证
	pswMd5 := util.MD5V([]byte(util.MD5V([]byte(password)) + user.Salt))
	if user.Password != pswMd5 {
		util.AddLoginlog(username, "登录失败:密码错误", clientIp, ipLocation, userAgent)
		return response.ErrLoginPassword
	}

	if (user.Username != "admin") && (user.Status != 1) {
		//return response.ErrRecordNotFound.SetMsg("当前账户被禁用")
		util.AddLoginlog(username, "登录失败:当前账户被禁用", clientIp, ipLocation, userAgent)
		return response.ErrRecordNotFound.SetMsg("登录异常，请联系管理员")
	}

	//global.LoggerOut(user.AllowIp)
	//global.LoggerOut(user.BanIp)

	arrAllowIp := util.GetIpData(user.AllowIp)
	arrBanIp := util.GetIpData(user.BanIp)

	arrAllowIp = util.UnionStringArrays(arrAllowIp, arrConfigAllowIp)
	arrBanIp = util.UnionStringArrays(arrBanIp, arrConfigBanIp)

	//global.LoggerOut(arrAllowIp)
	//global.LoggerOut(arrBanIp)

	// 授权ip验证登录
	if (user.Username != "admin") && (len(arrAllowIp) > 0 || len(arrBanIp) > 0) {
		// 授权ip
		if len(arrAllowIp) > 0 && util.IsExistsValue(arrAllowIp, "*") == false && util.IsExistsValue(arrAllowIp, clientIp) == false {
			//return response.ErrRecordNotFound.SetMsg("当前ip不允许登录"+clientIp)
			util.AddLoginlog(username, "登录失败:当前ip不允许登录", clientIp, ipLocation, userAgent)
			return response.ErrRecordNotFound.SetMsg("登录异常，请联系管理员")
		}
		// 禁止ip
		if len(arrBanIp) > 0 && util.IsExistsValue(arrBanIp, clientIp) == true {
			//return response.ErrRecordNotFound.SetMsg("当前ip禁止登录")
			util.AddLoginlog(username, "登录失败:当前ip禁止登录", clientIp, ipLocation, userAgent)
			return response.ErrRecordNotFound.SetMsg("登录异常，请联系管理员")
		}
		if len(arrBanIp) > 0 && util.IsExistsValue(arrBanIp, "*") == true {
			//return response.ErrRecordNotFound.SetMsg("当前账户禁止登录")
			util.AddLoginlog(username, "登录失败:当前账户禁止登录", clientIp, ipLocation, userAgent)
			return response.ErrRecordNotFound.SetMsg("登录异常，请联系管理员")
		}
	}

	// 签发token
	token := util.MD5V([]byte(user.UUID.String() + user.Username + strconv.Itoa(int(time.Now().Unix()))))
	key := fmt.Sprintf("fordeal:user:token:%s", token)
	_, err := global.RedisClient.HMSet(context.Background(), key, map[string]interface{}{
		"id":   user.ID,
		"uuid": user.UUID.String(),
	}).Result()
	if err != nil {
		return response.ErrSystem
	}
	global.RedisClient.Expire(context.Background(), key, time.Second*6*60*60)

	// 数据更新
	_ = global.Gorm.Model(&user).Updates(map[string]interface{}{
		"last_login_at": time.Now(),
		"last_login_ip": clientIp,
	})

	util.AddLoginlog(username, "登录成功", clientIp, ipLocation, userAgent)

	return response.OK.WithData(map[string]interface{}{
		"user":  user,
		"token": token,
	})
}

func (r *UserSrv) Logout(token string) response.Resp {
	key := fmt.Sprintf("fordeal:user:token:%s", token)
	if _, err := global.RedisClient.Del(context.Background(), key).Result(); err != nil {
		return response.ErrSystem
	}

	return response.OK
}

func (r *UserSrv) FindByUUID(uuid string) model.User {
	var user model.User
	_ = global.Gorm.Where("uuid = ?", uuid).First(&user).Error

	return user
}

func (r *UserSrv) FindByID(id uint, companyId uint) model.User {
	var user model.User
	//where := "1=1"
	//if companyId != 0 {
	//	where += " and `company_id`=" + strconv.Itoa(int(companyId))
	//}
	_ = global.Gorm.Where("id = ?", id).First(&user).Error

	return user
}

func (r UserSrv) FindByUsername(username string) model.User {
	var user model.User
	_ = global.Gorm.Where("`username` = ?", username).Find(&user).Error
	return user
}

func (r *UserSrv) Pagination(req request.UserPagination, operator model.User) response.Resp {
	offset := (req.PageIndex - 1) * req.PageSize
	where := "1=1"
	if !operator.SuperAdmin {
		where += fmt.Sprintf(" and `company_id`=%d", operator.CompanyId)
	}
	if req.Keyword != "" {
		where += " AND (`username` LIKE '%" + req.Keyword + "%' OR `uuid` LIKE '%" + req.Keyword + "%')"
	}
	if req.Status != nil && *req.Status != -1 {
		where += fmt.Sprintf(" AND `status`=%d", *req.Status)
	}
	var users []model.User
	err := global.Gorm.Preload("Company").Where(where).Offset(offset).Limit(req.PageSize).Find(&users).Error
	if err != nil {
		return response.ErrSystem
	}

	var total int64
	err = global.Gorm.Model(&model.User{}).Where(where).Count(&total).Error
	if err != nil {
		return response.ErrQuery
	}

	return response.OK.WithData(map[string]interface{}{
		"total": total,
		"list":  users,
	})
}

func (r *UserSrv) Update(id uint, req request.UserUpdate, operator model.User) response.Resp {
	user := r.FindByID(id, operator.CompanyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}
	updates := structs.Map(req)

	if err := global.Gorm.Model(&user).Updates(updates).Error; err != nil {
		return response.ErrStore
	}

	return response.OK
}

func (r *UserSrv) CurrentUpdate(req request.CurrentUserUpdate, operator model.User) response.Resp {
	user := r.FindByID(operator.ID, operator.CompanyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}
	updates := structs.Map(req)

	if err := global.Gorm.Model(&user).Updates(updates).Error; err != nil {
		return response.ErrStore
	}

	return response.OK
}

func (r *UserSrv) PasswordChange(user model.User, oldPsw, newPsw, confirmPsw string) response.Resp {
	// 验证原密码
	psw := util.MD5V([]byte(util.MD5V([]byte(oldPsw)) + user.Salt))
	if psw != user.Password {
		return response.ErrPswNotMatch
	}
	password := util.MD5V([]byte(util.MD5V([]byte(newPsw)) + user.Salt))
	updateData := map[string]interface{}{}
	if password != user.Password {
		updateData["password"] = password
	}
	// 验证新密码
	if newPsw != confirmPsw {
		return response.ErrUpdate.SetMsg("两次输入密码不一致！")
	}
	if err := global.Gorm.Model(&user).Updates(updateData).Error; err != nil {
		return response.ErrUpdate.SetMsg("密码修改失败！")
	}

	return response.OK
}

func (r *UserSrv) PasswordReset(uid uint, newPsw string, operator model.User) response.Resp {
	user := r.FindByID(uid, operator.CompanyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}
	password := util.MD5V([]byte(util.MD5V([]byte(newPsw)) + user.Salt))
	updateData := map[string]interface{}{}
	updateData["password"] = password
	if err := global.Gorm.Model(&user).Updates(updateData).Error; err != nil {
		return response.ErrUpdate.SetMsg("密码重置失败！")
	}

	return response.OK
}

func (r *UserSrv) UpdateIpLimit(uid uint, allowIp string, banIp string, operator model.User) response.Resp {
	user := r.FindByID(uid, operator.CompanyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}
	updateData := map[string]interface{}{}
	updateData["allow_ip"] = allowIp
	updateData["ban_ip"] = banIp
	if err := global.Gorm.Model(&user).Updates(updateData).Error; err != nil {
		return response.ErrUpdate.SetMsg("ip限制更新失败！")
	}

	return response.OK
}

func (r UserSrv) GetAllLimit() response.Resp {
	var configSrv ConfigSrv
	return configSrv.GetAllLimit()
}

func (r *UserSrv) UpdateAllLimit(allowIp string, banIp string, ipRemark string) response.Resp {
	var configSrv ConfigSrv
	return configSrv.UpdateAllLimit(allowIp, banIp, ipRemark)
}

func (r UserSrv) Create(user model.UserFillAble) response.Resp {
	user.Salt = util.RandomStr(16, "0123456789abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+<>,.")
	user.Password = util.MD5V([]byte(util.MD5V([]byte(user.Password)) + user.Salt))
	user.Status = 1
	user.UUID = uuid.NewV4()
	user.UUID = uuid.NewV4()

	userObj := model.User{
		UserFillAble: user,
	}
	if err := global.Gorm.Model(&model.User{}).Create(&userObj).Error; err != nil {
		return response.ErrCreate
	}

	return response.OK
}

func (r *UserSrv) appendChildren(menu *model.AntMenuTree, treeMap map[uint][]model.AntMenuTree) (err error) {
	menu.Children = treeMap[menu.ID]
	for i := 0; i < len(menu.Children); i++ {
		err = r.appendChildren(&menu.Children[i], treeMap)
	}

	return err
}

func (r UserSrv) Menus(uid uint, companyId uint) response.Resp {
	user := r.FindByID(uid, companyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}
	var menus []model.AntMenuTree
	// 超级管理员拥有所有菜单权限
	if uid == global.SuperAdminUserID {
		err := global.Gorm.Model(&model.Menu{}).
			Select("id, pid, name as text, type, icon, link, sort").
			Order("sort asc").
			Find(&menus).Error
		if err != nil {
			return response.ErrQuery
		}
		menusMap := make(map[uint][]model.AntMenuTree)
		for _, v := range menus {

			menusMap[v.Pid] = append(menusMap[v.Pid], v)
		}
		menuList := menusMap[0]
		for i := 0; i < len(menuList); i++ {
			err = r.appendChildren(&menuList[i], menusMap)
		}
		if err != nil {
			return response.ErrQuery.SetMsg("格式化菜单失败")
		}

		return response.OK.WithData(menuList)
	}

	// 其他用户
	// 1、获取用户角色
	roleIdStrList := casbinSrv.QueryUserRoleIds(user.UUID)
	var userRoles []uint
	for _, roleId := range roleIdStrList {
		roleIdInt, _ := strconv.Atoi(roleId.V1)
		userRoles = append(userRoles, uint(roleIdInt))
	}
	// 2、查询角色菜单权限
	type RoleMenus struct {
		MenuId uint `json:"menu_id" gorm:"column:menu_id"`
	}
	var roleMenus []RoleMenus
	err := global.Gorm.Table("role_menus").
		Joins("left join roles on role_menus.role_id=roles.id").
		Select("menu_id").
		Where("roles.id in ?", userRoles).
		Group("menu_id").Find(&roleMenus).Error
	if err != nil {
		return response.ErrQuery.SetMsg("角色菜单权限查询失败！")
	}
	var mIds []uint
	for _, mId := range roleMenus {
		mIds = append(mIds, mId.MenuId)
	}
	// 3、查询菜单
	err = global.Gorm.Model(&model.Menu{}).
		Where("id in ?", mIds).
		Select("id, pid, name as text, type, icon, link, sort").
		Order("sort asc").
		Find(&menus).Error
	if err != nil {
		return response.ErrQuery.SetMsg("角色菜单权限查询失败！")
	}
	menusMap := make(map[uint][]model.AntMenuTree)
	var pidMap []uint
	for _, v := range menus {
		pidMap = append(pidMap, v.Pid)
		menusMap[v.Pid] = append(menusMap[v.Pid], v)
	}

	menuList := menusMap[0]
	for i := 0; i < len(menuList); i++ {
		err = r.appendChildren(&menuList[i], menusMap)
	}
	if err != nil {
		return response.ErrQuery.SetMsg("格式化菜单失败")
	}
	return response.OK.WithData(menuList)
}

// Del 用户删除
func (r UserSrv) Del(uid uint, operator model.User) response.Resp {
	user := r.FindByID(uid, operator.CompanyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}
	// 数据删除
	if err := global.Gorm.Unscoped().Delete(&user).Error; err != nil {
		return response.ErrDel.SetMsg(err.Error())
	}
	// casbin_rule 删除
	res, _ := sEnforcer.DeleteRolesForUser(user.Username)
	if !res {
		return response.ErrDel.SetMsg("casbin_rule 删除失败")
	}

	return response.OK
}

func (r UserSrv) AddShopsForUser(id uint, shopIds []uint, operator model.User) response.Resp {
	user := r.FindByID(id, operator.CompanyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}
	// 排除非用户组织的店铺
	var actualShopIds []uint
	//_, companyShop := shopSrv.QueryShopIdsByUser(user)
	_, companyShop := shopSrv.QueryShopIdsByUserFull(user)
	for _, cs := range companyShop {
		for _, sid := range shopIds {
			if sid == cs {
				actualShopIds = append(actualShopIds, sid)
			}
		}
	}
	shopIdsJson, _ := json.Marshal(actualShopIds)
	err := global.Gorm.Model(&user).Update("shop_ids", shopIdsJson).Error
	if err != nil {
		return response.ErrQuery.SetMsg("角色店铺权限分配失败！")
	}

	return response.OK
}

func (r *UserSrv) Company(uid uint, operator model.User) response.Resp {
	user := r.FindByID(uid, operator.CompanyId)
	if user.ID == 0 {
		return response.ErrRecordNotFound
	}

	where := "1=1"
	//if user.CompanyId != 0 {
	//	where += fmt.Sprintf(" and `id`=%d", user.CompanyId)
	//}
	companyIds := util.GetUserCompanyIds(user)
	if len(companyIds) > 0 {
		where += fmt.Sprintf(" and `id` in (%s)", util.UintsToStr(companyIds, ","))
	}

	var companies []model.Company
	err := global.Gorm.Where(where).Find(&companies).Error
	if err != nil {
		return response.ErrQuery
	}
	return response.OK.WithData(companies)
}
